<div id="app">
  <p>{{title}}</p>
</div>

<script>
  // 传入信息，返回vnode，描述视图
  function h(tag, props, children) {
    return {
      tag,
      props,
      children
    }
  }

  // 1.基本结构
  const Vue = {
    // 扩展性
    createRenderer({
      querySelector,
      insert,
      createElement
    }) {
      // 返回渲染器
      return {
        createApp(options) {
          // 返回app实例
          return {
            mount(selector) {
              // console.log('mount!');
              // 1.找到宿主元素
              const parent = querySelector(selector)

              // 2.渲染页面
              if (!options.render) {
                //   2.1处理template：编译
                options.render = this.compile(parent.innerHTML)
              }

              // setup和其他选项
              if (options.setup) {
                this.setupState = options.setup()
              }
              if (options.data) {
                this.data = options.data()
              }

              // app.xxx
              const proxy = new Proxy(this, {
                get(target, key) {
                  // 先从setup中取，如果取不到再从data中取
                  if (target.setupState && key in target.setupState) {
                    return target.setupState[key]
                  } else {
                    return target.data[key]
                  }
                },
                set(target, key, val) {
                  if (target.setupState && key in target.setupState) {
                    target.setupState[key] = val
                  } else {
                    target.data[key] = val
                  }
                }
              })

              //   2.2用户直接编写render
              this.update = effect(() => {
                // const el = options.render.call(proxy)

                // // 3.追加到宿主
                // parent.innerHTML = ''
                // // parent.appendChild(el)
                // insert(el, parent)

                const vnode = options.render.call(proxy)
                // 转换vnode为dom
                // 初始化创建整棵树
                if (!this.isMounted) {
                  // 实现createElm，整体创建
                  const el = this.createElm(vnode)
                  parent.innerHTML = ''
                  insert(el, parent)

                  // init
                  this.isMounted = true
                } else {
                  // todo: update
                  this.patch(this._vnode, vnode)
                }
                this._vnode = vnode
              })
              // this.update()
            },
            patch(n1, n2) {
              const el = n2.el = n1.el
              // n1是老节点
              // n2是新节点
              // 1.更新：必须更新相同节点
              // 什么样的是相同节点
              // tag、key
              // sameVnode
              if (n1.tag === n2.tag && n1.key === n2.key) {
                // update
                // props todo
                // children 
                const oldCh = n1.children
                const newCh = n2.children

                if (typeof oldCh === 'string') {
                  if (typeof newCh === 'string') {
                    // text update
                    if (oldCh !== newCh) {
                      el.textContent = newCh
                    }
                  } else {
                    // 替换文本为一组子元素
                    // 清空再创建并追加
                    el.textContent = ''
                    newCh.forEach(child => insert(this.createElm(child), el))
                  }
                } else {
                  if (typeof newCh === 'string') {
                    // 替换一组子元素为文本
                    el.textContent = newCh
                  } else {

                  }
                }
              } else {
                // replace
              }
            },
            createElm(vnode) {
              const {
                tag,
                props,
                children
              } = vnode
              // 遍历vnode，创建整棵树
              const el = createElement(tag)
              // 如果存在属性，就设置他们
              // el.setAttribute(key, val)
              // 递归
              // 判断children是字符串
              if (typeof children === 'string') {
                el.textContent = children
              } else {
                children.forEach(child => insert(this.createElm(child), el))
              }
              // vnode中要保存真实dom，以备未来更新使用
              vnode.el = el
              return el
            },
            compile(template) {
              // 返回一个render函数
              // parse -> ast
              // generate -> ast=>render
              return function render() {
                if (Array.isArray(this.title)) {
                  return h('h3', null, this.title.map(s => h('p', null, s)))
                } else {
                  return h('h3', null, this.title)
                }

                // return h('h3', null, this.title)

                // return h('h3', null, [
                //   h('p', null, this.title),
                //   h('p', null, this.title),
                //   h('p', null, this.title),
                // ])
              }
            }
          }
        }
      }
    },
    createApp(options) {
      // 创建一个web平台特有渲染器
      const renderer = Vue.createRenderer({
        querySelector(sel) {
          return document.querySelector(sel)
        },
        insert(el, parent) {
          parent.appendChild(el)
        },
        createElement(tag) {
          return document.createElement(tag)
        }
      })
      return renderer.createApp(options)
    }
  }
</script>
<script>
  // 能够拦截用户对代理对象的访问
  // 从而在值发生变化的时候做出响应式
  function reactive(obj) {
    // vue2
    // Object.defineProperty(obj, {})
    // 缺点：性能、api、内部实现、数据结构支持
    return new Proxy(obj, {
      get(target, key) {
        console.log('get key:', key);
        // 建立依赖关系
        track(target, key)
        // console.log(targetMap);
        return target[key]
      },
      set(target, key, val) {
        console.log('set key:', key);
        target[key] = val
        // 获取依赖关系
        // 通知更新
        trigger(target, key)
        // app.update()
      }
    })
  }

  // 建立映射关系：依赖dep - 组件更新函数
  // vue2： 1组件有watcher
  // vue3：创建map结构 

  // 调用effect，首先执行fn
  const effectStack = []

  function effect(fn) {
    const eff = function () {
      try {
        effectStack.push(eff)
        fn()
      } finally {
        effectStack.pop()
      }
    }

    // 立即调用一次
    eff()

    return eff
  }

  // { 
  //   target1: { key: [update1, update2]} ,
  //   target2: { key: [update1, update2]} ,
  // }
  // const targetMap = new WeakMap()
  const targetMap = {}

  // 建立target,key和effectStack中存储的副作用函数之间的关系
  function track(target, key) {
    const effect = effectStack[effectStack.length - 1]
    // 判断target为key的对象存在不存在
    let map = targetMap[target]
    // targetMap.get(target)
    if (!map) {
      // 首次get这个target
      map = targetMap[target] = {}
      // targetMap.set(target, {})
    }

    let deps = map[key]
    if (!deps) {
      deps = map[key] = []
      // deps = map[key] = new Set()
    }
    // 映射关系建立
    // deps.add(effect)
    if (deps.indexOf(effect) === -1) {
      deps.push(effect)
    }
  }

  // 从targetMap中获取target，key对应的依赖函数并执行他们
  function trigger(target, key) {
    const map = targetMap[target]
    if (map) {
      const deps = map[key]

      if (deps) {
        deps.forEach(dep => dep())
      }
    }
  }

  // const obj = reactive({foo: 'foo', bar: 'bar'})
  // effect(() => {
  //   // 触发响应式数据
  //   console.log(obj.foo);
  // })
  // effect(() => {
  //   console.log(obj.foo,obj.bar);
  // })
  // obj.foo = 'foo~~~~~~'

  const app = Vue.createApp({
    setup() {
      // created
      const state = reactive({
        title: 'vue3, hello!'
      })
      setTimeout(() => {
        state.title = 'hello, vue3!'.split('')
      }, 2000)
      return state
    }
  })
  app.mount('#app')
</script>